package com.xafc.demo.security.config;


import com.xafc.demo.security.core.JwtAuthenticationTokenFilter;
import com.xafc.demo.security.handle.JwtAccessDeniedHandler;
import com.xafc.demo.security.handle.JwtAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


// securedEnabled = true ---> 开启角色权限控制，在访问的方法上需要增加@Secured注解进行权限控制，没有增加该注解，默认是不控制访问的。
// prePostEnabled = true ---> 开启基于access表达式进行权限控制，在访问的方法上需要增加@PreAuthorize注解进行权限控制，没有增加该注解，默认是不控制访问的。
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    private final JwtSecurityProperties jwtSecurityProperties;

    @Autowired
    public WebSecurityConfig(JwtAccessDeniedHandler jwtAccessDeniedHandler,
                             JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
                             JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter,
                             JwtSecurityProperties jwtSecurityProperties) {
        this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
        this.jwtAuthenticationTokenFilter = jwtAuthenticationTokenFilter;
        this.jwtSecurityProperties = jwtSecurityProperties;
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 关闭表单验证，关闭后UsernamePasswordAuthenticationFilter将不会执行
        http.formLogin().disable();

        // 授权
        http.authorizeRequests()
                // 放行静态资源文件
                .antMatchers("/js/**", "/css/**", "/images/**", "/plugins/**").permitAll()
                // 放行Swagger
                .antMatchers("/doc.html", "/webjars/**", "/swagger-resources", "/v2/api-docs/**").permitAll()
                // 放行/login登录页面, 不需要认证
                .antMatchers("/login").permitAll()
                // 其他的都需要认证才能访问
                .anyRequest().authenticated();

        // 授权异常
        http.exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .accessDeniedHandler(jwtAccessDeniedHandler);

        // 不创建会话
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 用户注销
        http.logout()
                .logoutSuccessUrl("/login")
                .invalidateHttpSession(true)
                .deleteCookies(jwtSecurityProperties.getCookieName());


        // 关闭csrf
        http.csrf().disable();

        // 允许跨域访问
        http.cors();

        //允许iframe
        http.headers().frameOptions().sameOrigin();

        // 定制我们自己的 session 策略：调整为让 Spring Security 不创建和使用 session
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/static/**");

    }

}
